package io.jee.medusa.orm;

import io.jee.medusa.orm.condition.Condition;
import io.jee.medusa.orm.condition.ConditionGroup;
import io.jee.medusa.orm.condition.ConditionValue;
import io.jee.medusa.orm.pager.Pager;
import io.jee.medusa.orm.utils.SQLUtils;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

@SuppressWarnings("unchecked")
public class Hibernate4DaoSupport<T> implements DaoSupport<T> {
	
	@Override
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	@Override
	public Session getSession() {
		return getSessionFactory().getCurrentSession();
	}
	
	@Override
	public Session currentSession() {
		return getSessionFactory().getCurrentSession();
	}

	@Override
	public Serializable save(T entity) {
		return getSession().save(entity);
	}
	
	@Override
	public void save(Collection<T> entitys) {
		if (entitys == null || entitys.size() == 0) {
			return;
		}
		for (T entity : entitys) {
			save(entity);
		}
	}

	@Override
	public void saveOrUpdate(T entity) {
		getSession().saveOrUpdate(entity);
	}

	@Override
	public void delete(T entity) {
		getSession().delete(entity);
	};

	@Override
	public int delete(String key, Object value) {
		String hql = "delete " + entityClass.getSimpleName() + " t0 where t0." + key + " = :" + key.replaceAll("\\.", "");
		return getSession().createQuery(hql).setParameter(key.replaceAll("\\.", ""), value).executeUpdate();
	}

	@Override
	public int delete(String key, Object[] values) {
		String hql = "delete " + entityClass.getSimpleName() + " t0 where t0." + key + " in (:" + key.replaceAll("\\.", "") + ")";
		return getSession().createQuery(hql).setParameterList(key.replaceAll("\\.", ""), values).executeUpdate();
	}

	@Override
	public void update(T entity) {
		getSession().update(entity);
	}

	@Override
	public void update(String key, Object value, Serializable id) {
		String hql = "update " + entityClass.getSimpleName() + " t0 set t0." + key + " = :" + key.replaceAll("\\.", "") + " where t0.id = :id";
		getSession().createQuery(hql).setParameter(key.replaceAll("\\.", ""), value).setParameter("id", id).executeUpdate();
	}

	@Override
	public T get(Serializable id) {
		return (T) getSession().get(entityClass, id);
	}

	@Override
	public T load(Serializable id) {
		return (T) getSession().load(entityClass, id);
	}

	@Override
	public T unique(String key, Object value) {
		String hql = "from " + entityClass.getSimpleName() + " t0 where t0." + key + " = :" + key.replaceAll("\\.", "");
		return (T) getSession().createQuery(hql).setParameter(key.replaceAll("\\.", ""), value).uniqueResult();
	}

	@Override
	public List<T> list(String key, Object value) {
		return list(key, value, null, null);
	}

	@Override
	public List<T> list(String key, Object value, String shortType, String shortProperty) {
		String hql = "from " + entityClass.getSimpleName() + " t0 where t0." + key + " = :" + key.replaceAll("\\.", "");
		if (shortProperty != null && shortProperty.trim().length() > 0) {
			if (shortType == null || shortType.trim().length() <= 0) {
				shortType = "desc";
			}
			hql += " order by t0." + shortProperty + " " + shortType;
		}
		return getSession().createQuery(hql).setParameter(key.replaceAll("\\.", ""), value).list();
	}

	@Override
	public List<T> list(String key, Object[] values) {
		String hql = "from " + entityClass.getSimpleName() + " t0 where t0." + key + " in (:" + key.replaceAll("\\.", "") + ")";
		return getSession().createQuery(hql).setParameterList(key.replaceAll("\\.", ""), values).list();
	}

	@Override
	public List<T> listAll() {
		Query query = getSession().createQuery("from " + entityClass.getSimpleName() + " t0");
		return query.list();
	}
	
	@Override
	public List<T> listAll(String orderType, String orderProperty) {
		Query query = getSession().createQuery("from " + entityClass.getSimpleName() + " t0 order by t0." + orderProperty + " " + orderType);
		return query.list();
	}

	@Override
	public long count(String key, Object value) {
		String hql = "select count(*) from " + entityClass.getSimpleName() + " t0 where t0." + key + " = :" + key.replaceAll("\\.", "");
		return (Long) getSession().createQuery(hql).setParameter(key.replaceAll("\\.", ""), value).uniqueResult();
	}

	@Override
	public long countAll() {
		Query query = getSession().createQuery("select count(*) from " + entityClass.getSimpleName() + " t0");
		return (Long) query.uniqueResult();
	}
	
	@Override
	public List<T> list(Map<String, Object> params) {
		StringBuffer sql = new StringBuffer("from " + entityClass.getSimpleName() + " t0");
		sql.append(SQLUtils.analyse(params));
		Query query = getSession().createQuery(sql.toString());
		setParam(query, params);
		return query.list();
	}

	@Override
	public List<T> list(ConditionGroup group) {
		StringBuffer sql = new StringBuffer("from " + entityClass.getSimpleName() + " t0");
		sql.append(SQLUtils.analyse(group, true));
		Query query = getSession().createQuery(sql.toString());
		setParam(query, group);
		return query.list();
	}

	@Override
	public Pager<T> list(Pager<T> pager, ConditionGroup group) {
		StringBuffer sql = new StringBuffer("from " + entityClass.getSimpleName() + " t0");
		sql.append(SQLUtils.analyse(group, true));
		Query query = getSession().createQuery(sql.toString());
		setParam(query, group);
		
		query.setFirstResult((pager.getPagenum() - 1) * pager.getPagesize());
		query.setMaxResults(pager.getPagesize());
		Long totalrecord = this.count(group);
		pager.setTotalrecord(totalrecord.intValue());
		pager.setLast(totalrecord.intValue() % pager.getPagesize() == 0 ? totalrecord.intValue() / pager.getPagesize() : totalrecord.intValue() / pager.getPagesize() + 1);
		pager.setTotalpage(pager.getLast());
		pager.setDatas(query.list());
		pager.setUpage(pager.getPagenum() <= 1 ? pager.getPagenum() : pager.getPagenum() - 1);
		pager.setNext(pager.getPagenum() + 1 > pager.getTotalpage() ? pager.getTotalpage() : pager.getPagenum() + 1);
		return pager;
	}

	@Override
	public Pager<T> list(Pager<T> pager, ConditionGroup group, String orderType, String orderProperty) {
		StringBuffer sql = new StringBuffer("from " + entityClass.getSimpleName() + " t0");
		sql.append(SQLUtils.analyse(group, true));
		
		sql.append(" order by ").append(orderProperty).append(" ").append(orderType);
		Query query = getSession().createQuery(sql.toString());
		setParam(query, group);
		
		query.setFirstResult((pager.getPagenum() - 1) * pager.getPagesize());
		query.setMaxResults(pager.getPagesize());
		Long totalrecord = this.count(group);
		pager.setTotalrecord(totalrecord.intValue());
		pager.setLast(totalrecord.intValue() % pager.getPagesize() == 0 ? totalrecord.intValue() / pager.getPagesize() : totalrecord.intValue() / pager.getPagesize() + 1);
		pager.setTotalpage(pager.getLast());
		pager.setDatas(query.list());
		pager.setUpage(pager.getPagenum() <= 1 ? pager.getPagenum() : pager.getPagenum() - 1);
		pager.setNext(pager.getPagenum() + 1 > pager.getTotalpage() ? pager.getTotalpage() : pager.getPagenum() + 1);
		return pager;
	}

	@Override
	public long count(ConditionGroup group) {
		StringBuffer sql = new StringBuffer("select count(*) from " + entityClass.getSimpleName() + " t0");
		sql.append(SQLUtils.analyse(group, true));
		Query query = getSession().createQuery(sql.toString());
		setParam(query, group);
		return (Long) query.uniqueResult();
	}

	@Override
	public long count(Map<String, Object> params) {
		StringBuffer sql = new StringBuffer("select count(*) from " + entityClass.getSimpleName() + " t0");
		sql.append(SQLUtils.analyse(params));
		Query query = getSession().createQuery(sql.toString());
		setParam(query, params);
		return (Long) query.uniqueResult();
	}
	
	@Override
	public Map<String, Object> excute(String sql, boolean isQuery) {
		Map<String, Object> result = new HashMap<String, Object>();
		// TODO 未实现执行SQL
		return result;
	}

	private void setParam(Query query, ConditionGroup group) {
		if (group == null) return;
		List<Condition> conditions = group.getConditions();
		List<ConditionGroup> groups = group.getGroups();
		
		ConditionValue conditionValue;
		for (Condition condition : conditions) {
			conditionValue = condition.getConditionValue();
			if (conditionValue.getOpType().equals(15)) {
				continue;
			}
			query.setParameter(conditionValue.getKey().replaceAll("\\.", ""), conditionValue.getValue());
		}
		
		for (ConditionGroup tmpGroup : groups) {
			setParam(query, tmpGroup);
		}
	}
	
	private void setParam(Query query, Map<String, Object> params) {
		if (params == null) return;
		for (String key : params.keySet()) {
			query.setParameter(key, params.get(key));
		}
	}
	
	protected final static String SHORT_TYPE_DESC = "DESC";
	protected final static String SHORT_TYPE_ASC = "ASC";
	private final Class<T> entityClass;
	private SessionFactory sessionFactory;
	
	@Resource
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	public Hibernate4DaoSupport() {
		this.entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}
	
}
